from abc import abstractmethod

import claripy
from archr.targets.actions import OpenChannelAction, SendAction, WaitAction, CloseChannelAction

class RexAction:
    """
    extend archr.action with a render function that translates each interaction action
    to code snippet
    """
    @abstractmethod
    def render(self, stype='py', script=''):
        """
        How this action should be translated in standalone exploits
        How to translate this action into standalone exploit
        """
        assert stype in ['c', 'py']
        if stype == 'c':
            marker = f"// {self.__class__.__name__}\n"
        elif stype == 'py':
            marker = f"# {self.__class__.__name__}\n"
        return marker+script+"\n"

class RexOpenChannelAction(RexAction, OpenChannelAction):
    """
    Extend OpenChannelAction with a render function
    """
    def render(self, stype='py', script=''):
        script = "r = nclib.Netcat((args.host, args.port), udp=False, verbose=True)"
        return super().render(stype=stype, script=script)

class RexSendAction(RexAction, SendAction):
    """
    Extend SendAction with a render function
    """
    def __init__(self, data, channel_name=None):
        super().__init__(data, channel_name)
        ident = "aeg_input_default"
        self.sim_data = claripy.Concat(*[claripy.BVS(ident+"_%x" % i, 8) for i in range(len(data))])
        self.concrete_data = None
        self.patches = []

    def perform(self):
        data = self.data
        new_data = data
        for x in self.patches:
            idx, inp = x
            new_data = new_data[:idx] + inp + new_data[idx+len(inp):]
        self.data = new_data
        try:
            super().perform()
        finally:
            self.data = data

    def render(self, stype='py', script=''):
        script = f"r.send({repr(self.concrete_data)})"
        return super().render(stype=stype, script=script)

class RexWaitAction(RexAction, WaitAction):
    """
    Extend WaitAction with a render function
    """
    def render(self, stype='py', script=''):
        script = f"time.sleep({self.seconds})"
        return super().render(stype=stype, script=script)

class RexCloseChannelAction(RexAction, CloseChannelAction):
    """
    Extend CloseChannelAction with a render function
    """
    def render(self, stype='py', script=''):
        super().render(stype=stype)
        script = "r.close()"
        return super().render(stype=stype, script=script)

class RexCommandAction(RexSendAction):
    """
    An action that interacts with remote shell after a shell is popped
    """
    def __init__(self, data, channel_name=None): #pylint:disable=super-init-not-called
        self.concrete_data = data
        self.sim_data = None
        self.data = data
